/**
 * 柯里化的柯里 curry（这个单词中文是咖喱。。）是用美国逻辑学大师Haskell Curry的名字命名的
 * 柯里化把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数，并且返回接受余下的参数且返回结果的新函数的技术
 */

// 正常的函数
function normalAdd(x, y) {
    return x + y;
}

normalAdd(1, 2) // 3

// 柯里化后的函数
function curriedAdd(x) {
    return function(y) {
        return x + y;
    }
}

curriedAdd(1)(2) // 3

// 如果要柯里化的函数参数很多，需要写很多return嵌套，可以简化成递归
// 比如说，这个函数如果柯里化需要写5层return嵌套，但是用通用的柯里化方法封装，一步调用就可以完成
function sum(a, b, c, d, e) {
    return a + b + c + d + e;
}
// 通用柯里化函数
function curry(fn) {
    // fn 直接把要柯里化的函数传入
    let val = []; // 记录传入的参数
    return function curriedFn(...args) {
        // fn.length 可以获取fn函数的形参的数量
        // ...args可以把入参转换成数组的格式
        val.push(...args) // 传入的参数数量，去比较原本函数的形参，小于说明还有参数要传入
        if (val.length < fn.length) {
            // 如果入参小于原本函数的形参，说明后续还有参数传入，继续返回方法接收
            return function(...args) {
                return curriedFn(...args)
            }
        }
        return fn(...val)
    }

    // 另一种写法
    return function curriedFn(...args) {
        if (args.length < fn.length) {
            return function() {
                // 这里的args可以存储，因为闭包的变量？
                console.log(args)
                return curriedFn(...args.concat(Array.from(arguments)))
            }
        }
        return fn(...args)
    }
}
curry(sum)(1)(2)(3)(4)(5)
curry(sum)(1, 2, 4)(3, 6) // 也兼容了一次传入多个参数的方式

